package;

import haxe.io.Eof;
import sys.io.File;
import sys.io.FileInput;
import sys.io.FileOutput;
import flixel.effects.particles.FlxEmitter;
import flixel.FlxBasic;
import flixel.FlxG;
import flixel.FlxSprite;
import flixel.FlxState;
import flixel.group.FlxTypedGroup;
import flixel.text.FlxText;
import flixel.ui.FlxButton;
import flixel.util.FlxColor;
import flixel.util.FlxPoint;
import flixel.tweens.FlxTween;
import flixel.util.FlxMath;
import Math;

/**
 * A FlxState which can be used for the actual gameplay.
 */

class PlayState extends FlxState
{
	/**
	 * Function that is called Up when to state is created to set it Up. 
	 */
	
//----Imagenes extra en el PlayState----

	private var _background:FlxSprite;
	 
//-----Variables utilizadas para la condicion de perder o ganar el juego----

	private var _restartWin:Bool = false;
	
	private var _restartLose:Bool = false;
	
	private var _gameOver:FlxText;
	
	private var _gameWon:FlxText;

//------Se utlizia para salir del Test en el modo Editor-------------------
	 
	private var _button1:FlxButton;
	
//-------Necesario para crear el Nivel-------------------------------------

    public var _Matriz:String;
	 
//----Necesarios para controlar el solapamiento de objetos--
	 
	public var punteroP:FlxSprite;
	public var punteroO:FlxSprite;	
	
	public var punteroB:FlxSprite;
	public var punteroC:FlxSprite;
	
//----Para el personaje-------------

	public var _Character:Jugador;

//----Bloques en el juego-----------

	public var _bloque:Bloques;
	
//----Plataformas en el juego-------

	public var _giratoria:Plataformas;
	
//----Groups----------------------------------------------

	public var _bloques:FlxTypedGroup<Bloques>;
	public var _plataformasGir:FlxTypedGroup<Plataformas>;
	public var _emisores:FlxTypedGroup<FlxEmitter>;
	public var _indicaReceptores:FlxTypedGroup<FlxSprite>;
	
//-----Necesarios para el movimiento del jugador y de los bloques----------

	private var _point:FlxPoint;		
	
	private var _movingBlock:FlxSprite;
	
	private var _moving:Bool = false;
	
//-------------------------------------------------------------------------
//-------------------------------------------------------------------------

	override public function create():Void
	{
		super.create();
		
//----Imagenes o animaciones extras en el PlayState-------

		_background = new FlxSprite(0, 0);
		_background.loadGraphic("assets/images/FondoV1.0_600x480_1Frame.png", false, 600, 480);
		add(_background);                                                                                        //Debe estar al fondo de toodooo
		
//-------Este Boque solo se utiliza si se esta Testeando un Nivel desde el Editor----
{		
		if (Reg.Testing)
		{
			_button1 = new FlxButton(610, 15,"End Test",TerminarTest);
			_button1.makeGraphic(30, 30, FlxColor.WHITE);
			add(_button1);
		}
		
		if (!Reg.Testing)
		{
			FlxG.mouse.visible = false;
		}
}		
//-------Variables iniciales necesarias-------		
		
		_movingBlock = null;
		
		_Character = new Jugador(100, 100);

//------Groups---------------------------------
{		
		_bloques = new FlxTypedGroup<Bloques>();
		_plataformasGir = new FlxTypedGroup<Plataformas>();
		_emisores = new FlxTypedGroup<FlxEmitter>();
		_indicaReceptores = new FlxTypedGroup<FlxSprite>();
}		
//---Necesarios para la plataforma Giratoria (Ver si pueden crearse y destruirse en girarBloque)------		
{		
		punteroP = new FlxSprite(100, 100);   // Cuadrado en centro de la plataforma
		punteroP.makeGraphic(10, 10, FlxColor.TRANSPARENT);
		
		punteroO = new FlxSprite(100, 100);   // Cuadrado en el centro del objeto
		punteroO.makeGraphic(2, 2, FlxColor.TRANSPARENT);
		
		punteroB = new FlxSprite(100, 100);   // Cuadrado en centro del Bloque
		punteroB.makeGraphic(15, 15, FlxColor.TRANSPARENT);
		
		punteroC = new FlxSprite(100, 100);   // Cuadrado en el frente del Personaje
		punteroC.makeGraphic(15, 15, FlxColor.TRANSPARENT);
}		
//------Crear todo el mapa en base a un .txt o un Array donde estan todos los nombres de los niveles en Reg.Level----		
		
		CrearMapa(Reg.Level[Reg.level]);
		
//------Utilizadas para el final del State (cuando se pierde, o se gana)-----
{
		_gameOver = new FlxText(FlxG.width / 3, FlxG.height / 2, FlxG.width, "Game Over", 40);
		_gameOver.visible = false;
		
		_gameWon = new FlxText(FlxG.width / 3, FlxG.height / 2, FlxG.width , "You Won!", 40);
		_gameWon.visible = false;
}
//----------------Lista de adds()--------
{
		//-Deben estar al Fondo de todo--
		add(_plataformasGir);
		
		add(_emisores);

		add(_bloques);		

		add(_indicaReceptores);		
		
		//--Debe estar ensima de todo---
		add(_Character);	
		
		add(punteroO);
		add(punteroP);
		
		add(punteroB);
		add(punteroC);
		
		add(_gameOver);
		add(_gameWon);
}
	}
	
	/**
	 * Function that is called when this state is destroyed - you might want to 
	 * consider setting all objects this state uses to null to help garbage collection.
	 */

//---------------------------------------------------------------------------
//---------------------------------------------------------------------------

	override public function destroy():Void
	{
		super.destroy();
	}

	/**
	 * Function that is called once every frame.
	 */
	
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------

	override public function update():Void
	{
		super.update();
		
//--------Todo acerca de mover el personaje y mover Bloques con el----------------		
		
		if (!(_restartWin || _restartLose))
		{
			move();
		}
		
//---------Boton "Control" (Ctrl) para suicidarse---------------------------

		if (FlxG.keys.justPressed.CONTROL)
		{
			Reg._grab = false;
			FlxG.resetState();
		}

//--------Fisica de Coliciones----------------------------------------------------
		
		FlxG.collide(_Character, _bloques);
		FlxG.collide(_bloques, _bloques);
		FlxG.collide(_Character, _bloques);	
		
//-----------Colicciones u Overlaps entre el laser y otras cosas---------------		
		
		FlxG.overlap(_emisores, _bloques, desintegrar);
		FlxG.overlap(_emisores, _bloques, Reg.opacar);
		FlxG.collide(_emisores, _indicaReceptores, ganar);
		FlxG.overlap(_emisores, _Character, perder);
		
//-----------Quienes giran en Plataformas Giratorias-------------
	
		girarBloque(_plataformasGir , _bloques);

//----Condiciones de Ganar o Perder----

		lose();
		win();
		
	}

//---------------------------------------------------------------------------
//TODAS LAS FUNCIONES QUE TIENEN QUE VER CON EL FINALIZAR UN ESTADO DE JUEGO ESTAN EN ESTE BLOQUE
//---------------------------------------------------------------------------
	
	private function lose():Void  //Luego de que se llama a la funcion perder esta es la que reinica el PlayState luego de apretar una tecla
	{
		if (_restartLose && FlxG.keys.justPressed.ANY)
		{
			FlxG.resetState();
			Restart();
		}
	}

//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
	
	private function win():Void  //Luego de que se llama a la funcion ganar esta es la que reinica el PlayState luego de apretar una tecla
	{
		if (_restartWin && FlxG.keys.justPressed.ANY)
		{
			Restart();
			FlxG.resetState();
		}
	}	

//---------------------------------------------------------------------------
//---------------------------------------------------------------------------

	private function ganar(foton:Fotones , receptor:FlxSprite):Void  //Cambia de color el receptor indicando que el laser llego a destino
	{
		if(foton._fotonColor == receptor.mass)
		{
			if (Reg.level < Reg.Level.length)
			{
				Reg.level += 1;
			}
			_restartWin = true;
			_gameWon.visible = true;
		}

		foton.exists = false;
	}

//---------------------------------------------------------------------------
//---------------------------------------------------------------------------

	private function perder(foton:Fotones , jugador:Jugador):Void   //Reinicia el juego cuando el personaje toca el laser
	{
		_gameOver.visible = true;
		_restartLose = true;
	}

//---------------------------------------------------------------------------
//---------------------------------------------------------------------------	

	public function Restart():Void //Setea todas las variables iniciales que estan en Reg, a sus valores iniciales
	{
		_movingBlock = null;
		Reg._grab = false;
	}
			
//---------------------------------------------------------------------------
//TODAS LAS FUNCIONES QUE TIENEN QUE VER CON EL MOVIMIENTO DEL PERSONAJE ESTAN EN ESTE BLOQUE
//---------------------------------------------------------------------------
	
	private function move():Void  //Mueve al personaje segun las teclas que el usuario esta apretando y tambien los bloques
	{
		if (FlxG.keys.justPressed.SPACE || FlxG.keys.justPressed.P)
		{
			for (bloque in _bloques)
			{	
				if (bloque._esDinamico)
				{
					punteroC.x = _Character.x + Reg._altoC / 2 - 5;
					punteroC.y = _Character.y + Reg._altoC / 2 - 5;

					punteroB.x = bloque.x + Reg._altoB / 2 - 5;
					punteroB.y = bloque.y + Reg._altoB / 2 - 5;
					
					if (_Character._direccion == "Down")
					{
						punteroC.y += Reg._altoC / 2 + Reg._altoB / 2;
					}
					else 
					{
						if (_Character._direccion == "Up")
						 {
							punteroC.y -= Reg._altoC / 2 + Reg._altoB / 2;
						 }
					}

					if (_Character._direccion == "Left")
					{
						punteroC.x -= Reg._altoC / 2 + Reg._altoB / 2;
					}
					else
					{
						if (_Character._direccion == "Right")
						{	
							punteroC.x += Reg._altoC / 2 + Reg._altoB / 2;
						}
					}
				
					if (FlxG.overlap(punteroC, punteroB))   //Por esta razon hay un extraño Bug que hace que el movimiento no funcione cuando se agarra a un bloque de costado y no desde el centro
					{											//El Bug es devido a que no se porque toma que los punteros se solapan cuando no es asi y luego no lo vuelve a tomar mas
						bloque.immovable = !bloque.immovable;
						Reg._grab = !Reg._grab;
						_movingBlock = bloque;
					}
					else
					{
						bloque.immovable = true;
					}
				}
			}	
		}
					
		if (_movingBlock != null)
		{
			_movingBlock.velocity.set(0, 0);
		}
		
		if (!Reg._grab)
		{
			if (FlxG.keys.pressed.UP || FlxG.keys.pressed.W)
			{
				_Character._direccion = "Up";
				_Character.velocity.y = -1 * Reg._charVelocity;
			}
			else
			{
				if (FlxG.keys.pressed.DOWN || FlxG.keys.pressed.S)
				{
					_Character._direccion = "Down";
					_Character.velocity.y = Reg._charVelocity;
				}
				else 
				{
					if (FlxG.keys.pressed.LEFT || FlxG.keys.pressed.A)
					{
						_Character._direccion = "Left";
						_Character.velocity.x = -1 * Reg._charVelocity;
					}				
					else 
					{
						if (FlxG.keys.pressed.RIGHT || FlxG.keys.pressed.D)
						{
							_Character._direccion = "Right";
							_Character.velocity.x = Reg._charVelocity;
						}
					}
				}
			}
		}
		else if (Reg._grab && !_moving)
		{
			if (FlxG.keys.pressed.LEFT && (_Character._direccion == "Left" || _Character._direccion == "Right"))
				move2(_Character, _movingBlock, _bloques, "Left", _Character._direccion);
			else if (FlxG.keys.pressed.RIGHT && (_Character._direccion == "Left" || _Character._direccion == "Right"))
				move2(_Character, _movingBlock, _bloques, "Right", _Character._direccion);
			else if(FlxG.keys.pressed.UP && (_Character._direccion == "Up" || _Character._direccion == "Down"))
				move2(_Character, _movingBlock, _bloques, "Up", _Character._direccion);
			else if(FlxG.keys.pressed.DOWN && (_Character._direccion == "Up" || _Character._direccion == "Down"))
				move2(_Character, _movingBlock, _bloques, "Down", _Character._direccion);
		}
	}

//---------------------------------------------------------------------------
//---------------------------------------------------------------------------

	private function move2(char:FlxSprite,block:FlxSprite,blocks:FlxTypedGroup<Bloques>,movement:String, orient:String):Void
	{
		var x:Int = 0;
		var y:Int = 0;

		var point:FlxPoint = new FlxPoint(Reg._altoB/2,Reg._altoB/2);
		for (i in 0...10)
		{
			for (j in 0...8)
			{
				point.set(Reg._altoB/2 + Reg._altoB * i, Reg._altoB/2 + Reg._altoB * j);
				if (block.overlapsPoint(point))
				{
					x = i;
					y = j;
				}
			}
		}
		if (movement == "Left")
		{
			if (orient == "Left")
				point.set(Reg._altoB/2 + Reg._altoB *(x - 1), Reg._altoB/2 + Reg._altoB * y);
			else if (orient == "Right")
				point.set(Reg._altoB/2 + Reg._altoB *(x - 2), Reg._altoB/2 + Reg._altoB * y); 
		}
		else if (movement == "Right")
		{
			if (orient == "Right")
				point.set(Reg._altoB/2 + Reg._altoB *(x + 1), Reg._altoB/2 + Reg._altoB * y);
			else if (orient == "Left")
				point.set(Reg._altoB/2 + Reg._altoB *(x + 2), Reg._altoB/2 + Reg._altoB * y); 
		}
		else if (movement == "Up")
		{
			if (orient == "Up")
				point.set(Reg._altoB/2 + Reg._altoB *x, Reg._altoB/2 + Reg._altoB * (y-1));
			else if (orient == "Down")
				point.set(Reg._altoB/2 + Reg._altoB *x, Reg._altoB/2 + Reg._altoB * (y-2)); 
		}
		else if (movement == "Down")
		{
			if (orient == "Down")
				point.set(Reg._altoB/2 + Reg._altoB *x, Reg._altoB/2 + Reg._altoB * (y+1));
			else if (orient == "Up")
				point.set(Reg._altoB/2 + Reg._altoB *x, Reg._altoB/2 + Reg._altoB * (y+2)); 
		}
		
		for (block in blocks)
		{
			if (block.overlapsPoint(point))
				return;
		}
		
		_moving = true;

		if (movement == "Left")
		{
			var tween = FlxTween.tween(char,{x:char.x-Reg._altoB},0.2,{complete:finishMove});
			var tween = FlxTween.tween(block,{x:block.x-Reg._altoB},0.2);
		}
		else if (movement == "Right")
		{
			var tween = FlxTween.tween(char,{x:char.x+Reg._altoB},0.2,{complete:finishMove});
			var tween = FlxTween.tween(block,{x:block.x+Reg._altoB},0.2);
		}
		else if (movement == "Up")
		{
			var tween = FlxTween.tween(char,{y:char.y-Reg._altoB},0.2,{complete:finishMove});
			var tween = FlxTween.tween(block,{y:block.y-Reg._altoB},0.2);
		}
		else if (movement == "Down")
		{
			var tween = FlxTween.tween(char,{y:char.y+Reg._altoB},0.2,{complete:finishMove});
			var tween = FlxTween.tween(block,{y:block.y+Reg._altoB},0.2);
		}
	}

//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
	
	private function finishMove(tween:FlxTween):Void
	{
		_moving = false;
	}
	
//---------------------------------------------------------------------------
//TODAS LAS FUNCIONES QUE TIENEN QUE VER CON LA RELACION ENTRE RECURSOS ESTAN EN ESTE BLOQUE
//---------------------------------------------------------------------------
		
	public function desintegrar(foton:Fotones, bloque:Bloques):Void
	{
		if (bloque._tipo == "Rompible")
		{
			bloque._vida--;
		}
		if (Reg._grab && _movingBlock.exists == false)
		{
			Reg._grab = false;
		}
	}

//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
	
	private function girarBloque(plataformas:FlxTypedGroup<Plataformas>, bloques:FlxTypedGroup<Bloques>):Void //Si un Bloque esta perfectamente ensima de una plataforma Giratoria lo gira
	{
		for (bloque in bloques)
		{	
			for (plataforma in  plataformas)
			{
				punteroP.setPosition(plataforma.x + Reg._altoP / 2 - 1, plataforma.y + Reg._altoP / 2 - 1);
				punteroO.setPosition(bloque.x + bloque._altoB / 2 - 1, bloque.y + bloque._altoB / 2 - 1);
				
				if(!Reg._grab && (bloque._girando || !bloque._giro))
				{	
						if (FlxG.overlap(punteroO,punteroP))
						{
							if (plataforma._sentido == 0)
							{
								bloque._angulo -= bloque._velocidadAngular;
							}
							else
							{
								bloque._angulo += bloque._velocidadAngular;
							}
							
							if (!bloque._giro)
							{
								bloque._girando = true;
							}
							
							if (!bloque._giro || !bloque._girando)
							{
								bloque._giro = true;
								bloque._timerGirot.start(Reg._tiempoGiro, bloque.girando, 1);  //Tiempo que tarda en girar Pi/2
								bloque._timerGiroT.start(Reg._periodoGiro, bloque.giro, 1);
							}
						}
	//					punteroP.setPosition(-10,-10);
	//					punteroO.setPosition(-10,-10);
				}
			}
		}
	}
	
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
	
	public function TerminarTest():Void //Vuelve al EditorState para continuar con la edicion o guardar el Nivel
	{
		Reg._grab = false;
		FlxG.switchState(new EditorState());
	}

//---------------------------------------------------------------------------
//---------------------------------------------------------------------------

	public function CrearMapa(name:String):Void //Crea todo el mapa con el nombre del String que se pasa como Argumento [Debe estar en un archivo .txt en la carpeta /assets/data (No agregarle el .txt al nombre)]
	{	
		var i:Int = 0;
		var posX:Int = 0;
		var posY:Int = 0;
		
		var direccion:Int;
		
		if (Reg.Testing)
		{
			_Matriz = Reg.DefaultLevel;
		}
		else
		{
			_Matriz = File.getContent("assets/data/" + name + ".txt");
		}
		
		while(_Matriz.charAt(i) != '-')
		{
			if (_Matriz.charAt(i) == 'P') //Pared
			{
				_bloque = new Bloques(posX * Reg._altoB, posY * Reg._altoB, false, "Pared",0,0);
				_bloques.add(_bloque);
			}

			if (_Matriz.charAt(i) == 'B') //Rompible (B de Breakable)
			{
				_bloque = new Bloques(posX * Reg._altoB, posY * Reg._altoB,true, "Rompible",0,0);
				_bloques.add(_bloque);
			}

			if(_Matriz.charAt(i) == '\n') //Enter
			{
				posY++;
				posX = 0;
			}
			
			if(_Matriz.charAt(i) == ',') //Espacio
			{
				posX++;	
			}
			
			if (_Matriz.charAt(i) == 'J') //Jugador
			{
				_Character.setPosition(posX * Reg._altoB + 15, posY * Reg._altoB + 15);
			}
			
			if(_Matriz.charAt(i) == 'E') //Emisor , Direccion, Color (R:0,B:1,G:2)
			{
				i++;   //Porque se escribe, por ej "-E1R-" y se lee que es un Emisor con direccion 1, color Rojo
				
				direccion = Std.parseInt(_Matriz.charAt(i));
				
				i++;
				
				_bloque = new Bloques(posX * Reg._altoB, posY * Reg._altoB, true, "Emisor",direccion,Std.parseInt(_Matriz.charAt(i)));
				_bloques.add(_bloque);
				_emisores.add(_bloque._emitter);
			}
			
			 
			if(_Matriz.charAt(i) == 'R')  //Receptor , Direccion
			{
				i++;   //Porque se escribe, por ej "-R12-" y se lee que es un Receptor con direccion 1 y color 2
				
				direccion = Std.parseInt(_Matriz.charAt(i));
				
				i++;
					
				_bloque = new Bloques(posX * Reg._altoB, posY * Reg._altoB, false, "Receptor",direccion,Std.parseInt(_Matriz.charAt(i)));
				_bloques.add(_bloque);
				_indicaReceptores.add(_bloque._indicador);
			}
			
			
			if(_Matriz.charAt(i) == 'G')  //Giratoria , Sentido (0:Antihorario, 1:Horario)
			{
				i++;   //Porque se escribe, por ej "-G0-" y se lee que es una plataforma Giratoria de sentido Antihorario
					
				_giratoria = new Plataformas(posX * Reg._altoB, posY * Reg._altoB,"Giratoria",Std.parseInt(_Matriz.charAt(i)));
				_plataformasGir.add(_giratoria);
			}
			
		 	i++;
			
		}
		
	}

//---------------------------------------------------------------------------
//---------------------------------------------------------------------------


}
